export interface XMLNode {
    nodeName: string;
    attributes?: any[];
    children?: XMLNode[];
}
export class DOMParser {
    protected index = 0;
    protected xml: string;

    constructor() {
        
    }

    public parseFromString(xml: string): XMLNode {
        this.xml = `<document>`+xml.replace(/<\?xml.*?\?>\s*/s, '')
                     .replace(/\n\s+/g, ' ') // 标准化空白字符
                     .trim() + `</document>`;
        return this.parseNode();
    }

    private parseNode(): XMLNode {
        this.consume('<');
        const tagName = this.parseTagName();
        const attributes = this.parseAttributes();
        const node: XMLNode = { nodeName: tagName, children:[], attributes:[]};

        if (attributes.length > 0) {
            node.attributes = attributes;
        }

        // 处理自闭合标签
        if (this.peek() === '/') {
            this.consume('/');
            this.consume('>');
            return node;
        }

        this.consume('>');
        node.children = this.parseChildren(tagName);
        return node;
    }

    private parseTagName(): string {
        let tagName = '';
        while (/[^\s/>]/.test(this.peek())) {
            tagName += this.xml[this.index++];
        }
        return tagName;
    }

    private parseAttributes(): any[] {
        const attributes: any[] = [];
        while (this.peek() !== '>' && this.peek() !== '/') {
            this.consumeWhitespace();
            const localName = this.parseAttributeKey();
            this.consume('=');
            const value = this.parseAttributeValue();
            attributes[attributes.length] = {localName:localName, value:value};
        }
        return attributes;
    }

    private parseAttributeKey(): string {
        let key = '';
        while (/[^\s=]/.test(this.peek())) {
            key += this.xml[this.index++];
        }
        return key;
    }

    private parseAttributeValue(): string {
        const quote = this.consume();
        let value = '';
        while (this.peek() !== quote) {
            value += this.xml[this.index++];
        }
        this.consume(quote);
        return value;
    }

    private parseChildren(parentTag: string): XMLNode[] {
        const children: XMLNode[] = [];
        while (true) {
            this.consumeWhitespace();
            
            // 增强结束标签检测
            if (this.checkClosingTag(parentTag)) {
                break;
            }
            
            if (this.peek() === '<') {
                children.push(this.parseNode());
            } else {
                // 跳过文本内容（可选保留逻辑）
                this.index++;
            }
        }
        return children;
    }

    private checkClosingTag(parentTag: string): boolean {
        if (this.peek() === '<' && this.xml[this.index + 1] === '/') {
            const closingIndex = this.index;
            this.consume('<');
            this.consume('/');
            this.consumeWhitespace();
            const endTag = this.parseTagName();
            this.consumeWhitespace();
            this.consume('>');
            
            if (endTag !== parentTag) {
                throw new Error(`标签不匹配: 期待 ${parentTag} 实际 ${endTag}`);
            }
            return true;
        }
        return false;
    }

    protected consume(char?: string): string {
        if (char && this.xml[this.index] !== char) {
            throw new Error(`在位置 ${this.index} 期待 "${char}" 但发现 "${this.xml[this.index]}"`);
        }
        return this.xml[this.index++];
    }

    protected consumeWhitespace() {
        while (/\s/.test(this.peek())) {
            this.index++;
        }
    }

    protected peek(): string {
        return this.xml[this.index] || '';
    }
}
// let obj=(new DOMParser).parseFromString(`<?xml version="1.0" encoding="utf-8"?>
// <component size="161,161" extention="Button">
//   <Button/>
//   <controller name="button" pages="0,up,1,down,2,over,3,selectedOver"/>
//   <displayList>
//     <image id="n0_gl7v" src="hcice" name="n0" xy="0,0">
//       <relation target="" sidePair="width,height"/>
//     </image>
//   </displayList>
// </component>`);
// App.Alert(JSON.stringify(obj));
